//
//  zen_font.hpp
//  font
//
//  Created by MeherTj on 2021/7/7.
//  Copyright © 2021 lover. All rights reserved.
//

#pragma once

#include <vector>
#include <map>
#include <memory>
#include "zen_truetype.h"

#include "zen_exception.h"
#include "zen_image.h"
#include "zen_cache.h"

namespace Zen {
	
	struct FontChar
	{
		uint32_t unicode = 0;
		std::vector<uint8_t> bitmap;
		
		int width = 0;
		int height = 0;
		
		int bitmap_x = 0; /// move x. (left:-)
		int bitmap_y = 0; /// move ? from baseline. (top:-)
		
		int x_advance = 0; /// area width.
		int y_advance = 0; /// line height.
	};
	
	struct FontInfo {
		float height = 0;
		float ascent = 0;
		float lineHeight = 0;
	};
	
	struct FontBrushInfo {
		float height = 0;
		float ascent = 0;
		float lineHeight = 0;
		float scale = 0;
		
	};
	
	class Font
	{
	protected:
		Font() = default;
		virtual ~Font() = default;
	public:
		
		virtual std::shared_ptr<FontChar> getBitmap(uint32_t unicode, size_t height) = 0;
		
		virtual FontInfo const & getFontInfo() = 0;

	public:
		static std::shared_ptr<Font> Create(std::vector<uint8_t> const & data);
	};
	
	class FontBrush
	{
	public:
		static std::shared_ptr<FontBrush> Create(std::shared_ptr<Font> font, size_t height);
		
		virtual std::shared_ptr<FontChar> getCharBitmap(uint32_t unicode) = 0;
		
		virtual std::shared_ptr<Font> getFont() const = 0;
		
		virtual FontBrushInfo const & getBrushInfo() const = 0;
		
		virtual size_t getHeight() = 0;
	public:
		virtual ~FontBrush() = default;
		
	protected:
		FontBrush() = default;
	};
	
	class FontCache : protected Cache<std::string, std::shared_ptr<Font> >
	{
	public:
		static FontCache * Get();
		
		void cacheFont(std::string const & name, std::shared_ptr<Font> font) { cache(name, font); }

		std::shared_ptr<Font> getFont(std::string const & name) { return load(name); }
		
		using Cache::clearCache;
	protected:
		std::shared_ptr<Font> onCacheNotFound(std::string const & key) override { return nullptr; }

	};
}

namespace Zen {
	
	class FontRender
	{
	public:
		struct Style {
			float alignment = 0.f; // [0., 1.] 0:left, 1:right
			int word_space = 0; // [-,+] px as font-size
			int line_space = 0; // [-,+] px
			int width_limit = 0; // [0, +] 0:nolimit, >0: px
		};
		/**
		 @struct RenderSetting
		 @var x_edge, y_edge
		 @var underline
		 range: [-,+] in px,
		 if underline
		 < 0, the UNDERLINE will be ABOVE base line.
		 = 0, no line.
		 = 1, on base line.
		 > 1, on base line and BELOW it.
		 */
		struct RenderSetting {
			int x_off = 0; /// [-,+] move text l->r
			int y_off = 0; /// [-,+] move text top->bot
			int underline = 0;
			uint8_t fill = 0; /// bg_fill.
		};
		
		/// record a line
		struct LineInfo
		{
			int lefts_limit = 0; // most left pixel pos.
			int right_limit = 0;
			std::vector<std::shared_ptr<FontChar> > chars;
		};
	public:
		static std::shared_ptr<FontRender> Create
		(std::shared_ptr<FontBrush>, std::u32string const & text, Style const & style);
		
		virtual std::shared_ptr<Image> renderImage
		(size_t width, size_t height, RenderSetting const & set) = 0;
		
		virtual size_t width() = 0;
		
		virtual size_t height() = 0;
		
		virtual size_t lineCount() = 0;
		
		virtual std::vector<LineInfo> const & getLines() = 0;
	};

	/// tile map
	class FontTileMap {
	public:
		struct CharInfo {
			int x, y, w, h;
			float x_off, y_off;
		};

		/// record a line
		struct LineInfo
		{
			int width = 0;
			std::vector<std::shared_ptr<FontChar> > chars;
		};
	public:
		static std::shared_ptr<FontTileMap> Create
		(std::shared_ptr<FontBrush>, std::u32string const & text, size_t x_count);

		virtual std::shared_ptr<Image> renderImage(size_t width, size_t height) = 0;

		virtual size_t width() = 0;

		virtual size_t height() = 0;

		virtual size_t lineCount() = 0;

		virtual std::vector<CharInfo> const & getCharInfos() = 0;

		virtual std::vector<LineInfo> const & getLines() = 0;
	};
}
